package edu.usc.csci578.util;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import edu.usc.csci578.exceptions.*;
import edu.usc.csci578.manager.ModelManager;
import edu.usc.csci578.model.*;

public class ModelReader {
	private static ModelReader mr = null;
	private ModelManager mm = null;

	private ModelReader() {
		mm = ModelManager.getInstance();
	}
	
	public static ModelReader getInstance() {
		if (mr == null) {
			mr = new ModelReader();
		}
		return mr;
	}
		
	public void load() {
		mm.clear();

		try {
			loadMethod();
			//mm.turnOff();
			loadSpecified();
			loadGraph();
			fullyConnect();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void loadMethod() throws Exception {
		DocumentBuilder db = DocumentBuilderFactory.newInstance()
				.newDocumentBuilder();
		Document doc = db.parse(new File(Configure.METHODINFO_XML_PATH));
		Element root = doc.getDocumentElement();
		NodeList clusterNodes = root.getChildNodes();
		for (int i = 0; i < clusterNodes.getLength(); i++) {
			if (clusterNodes.item(i) instanceof Element) {
				Element clusterNode = (Element) clusterNodes.item(i);

				Cluster cluster = mm.getCluster(clusterNode.getAttribute("name"),true);
				NodeList classNodes = clusterNode.getElementsByTagName("class");

				for (int j = 0; j < classNodes.getLength(); j++) {
					Element classNode = (Element) classNodes.item(j);

					SClass aClass = mm.getClass(classNode.getAttribute("name"),true);
					NodeList methodNodes = classNode
							.getElementsByTagName("method");

					for (int k = 0; k < methodNodes.getLength(); k++) {
						Element methodNode = (Element) methodNodes.item(k);

						SMethod method = mm.getMethod(methodNode.getAttribute("name"),true);

						NodeList paraNodes = methodNode
								.getElementsByTagName("param");
						for (int l = 0; l < paraNodes.getLength(); l++) {
							if (paraNodes.item(l) instanceof Element) {
								Element paraNode = (Element) paraNodes.item(l);
								SClass para = mm.getClass(paraNode.getAttribute("name"),true);
								if (!method.addParas(para)) {
									throw new Exception("Oh my class");
								}
							}
						}

						NodeList retvalNodes = methodNode
								.getElementsByTagName("retval");
						Element retvalNode = (Element) retvalNodes.item(0);
						SClass retval = mm.getClass(retvalNode.getAttribute("name"),true);
						if (retval == null) {
							throw new Exception("Shit XML: retval == null");
						}
						method.setRetval(retval);

						aClass.add(method);
					}
					cluster.add(aClass);
				}
			}
		}
	}

	private void loadSpecified() throws Exception {
		DocumentBuilder db = DocumentBuilderFactory.newInstance()
				.newDocumentBuilder();
		Document doc = db.parse(new File(Configure.SPECIFIED_XML_PATH));
		Element root = doc.getDocumentElement();
		NodeList childNodes = root.getChildNodes();
		for (int x = 0; x < childNodes.getLength(); x++) {
			if (childNodes.item(x) instanceof Element) {
				Element childNode = (Element) childNodes.item(x);
				if (childNode.getTagName().equals("topics")) {
					NodeList topicElements = childNode
							.getElementsByTagName("topic");
					for (int i = 0; i < topicElements.getLength(); i++) {
						if (topicElements.item(i) instanceof Element) {
							Element topicElement = (Element) topicElements
									.item(i);
							Integer id = Integer.parseInt(topicElement
									.getAttribute("id"));
							Boolean type = ModelParser.isSpec(topicElement
									.getAttribute("type"));
							List<String> words = new ArrayList<String>();
							NodeList wordNodes = topicElement
									.getElementsByTagName("word");
							for (int j = 0; j < wordNodes.getLength(); j++) {
								words.add(wordNodes.item(j).getTextContent());
							}
							STopic topic = mm.getTopic(id, true);	
							//In case of Topics not necessarily in the beginning
							topic.setType(type);
							topic.setWords(words);
						}
					}
				} else if (childNode.getTagName().equals("Cluster")) {
					NodeList nameList = childNode.getElementsByTagName("name");
					String name = nameList.item(0).getTextContent();
					if (name == null) {
						throw new Exception("Cluster: null name");
					}
					String type = childNode.getAttribute("type");

					Cluster cluster = mm.getCluster(name.trim(),false);
					if (cluster == null) {
						System.err.println(name);
					}
					cluster.setType(ModelParser.isClusterSpec(type));

					NodeList classNodes = childNode
							.getElementsByTagName("class");
					for (int i = 0; i < classNodes.getLength(); i++) {
						if (classNodes.item(i) instanceof Element) {
							Element classNode = (Element) classNodes.item(i);
							SClass aClass = mm.getClass(classNode.getFirstChild().getNodeValue().trim(),false);
							if (aClass == null) {
								throw new Exception("Class Check: "
										+ classNode.getFirstChild()
												.getNodeValue());
							}

							NodeList methodNodes = classNode
									.getElementsByTagName("method");
							for (int j = 0; j < methodNodes.getLength(); j++) {
								if (methodNodes.item(j) instanceof Element) {
									Element methodNode = (Element) methodNodes
											.item(j);
									SMethod method = mm.getMethod(methodNode.getFirstChild().getNodeValue().trim(), false);
									if (method == null) {
										throw new Exception("Exception: "+ methodNode.getFirstChild().getNodeValue().trim());
									}
								}
							}
						}
					}

					NodeList docTopicList = childNode
							.getElementsByTagName("topic");
					for (int i = 0; i < docTopicList.getLength(); i++) {
						if (docTopicList.item(i) instanceof Element) {
							Element docTopicElement = (Element) docTopicList
									.item(i);
							int topicId = Integer.parseInt(docTopicElement
									.getAttribute("id"));
							STopic topic = mm.getTopic(topicId, true);
							double prob = Double.parseDouble(docTopicElement
									.getTextContent());
							if (topic == null) {
								throw new Exception("ModelReader: Null Topic");
							}
							cluster.add(topic, prob);
						}
					}
				}
			}
		}
	}

	private void loadGraph() throws Exception {
		DocumentBuilder db = DocumentBuilderFactory.newInstance()
				.newDocumentBuilder();
		Document doc = db.parse(new File(Configure.GRAPH_XML_PATH));
		Element root = doc.getDocumentElement();
		NodeList ceNodes = root.getElementsByTagName("ClusterEdge");
		for (int i = 0; i < ceNodes.getLength(); i++) {
			if (ceNodes.item(i) instanceof Element) {
				Element ceNode = (Element) ceNodes.item(i);
				String src = ceNode.getElementsByTagName("src").item(0)
						.getFirstChild().getNodeValue().trim();
				String tgt = ceNode.getElementsByTagName("tgt").item(0)
						.getFirstChild().getNodeValue().trim();
				mm.addClusterEdge(src, tgt, false);
			}
		}
	}
	private void fullyConnect() throws Exception
	{
		int count = 0;
		int countError = 0;
		
		for(Cluster cluster : mm.getAllClusters())
		{
			Vector<String> subs;
			subs = ModelParser.parseClusterName(cluster.getName());
			for(int i=0;i<subs.size();i++)
			{
				try
				{
					count++;
					cluster.add(mm.getCluster(subs.elementAt(i), false));
				} catch (ClusterDontExistException e)
				{
					countError++;
					//throw e;
				}
			}
		}
		System.out.println(countError + " of " + count + " subClusters don't exist");
	}
}
